package net.yuanmomo.socket.nio;

import net.yuanmomo.socket.Constant;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * Created by Hongbin.Yuan on 2016-07-04 00:29.
 */

public class MultiplexerTimeServer implements Runnable {
    private Selector selector;
    private ServerSocketChannel server;
    private volatile boolean stop;

    public MultiplexerTimeServer(int port) {

        ServerSocketChannel server = null;
        try {
            // 创建 Reactor线程,创建多路复用器,并启动线程
            Selector selector = Selector.open();

            // 打开 ServerSocketChannel , 监听客户端的连接。
            server = ServerSocketChannel.open();
            // 绑定端口参数,设置非阻塞模式
            server.socket().bind(new InetSocketAddress(InetAddress.getByName("IP"), port));
            server.configureBlocking(false);

            server.register(selector, SelectionKey.OP_ACCEPT);

            System.out.println(String.format("Server is running on PORT [%s].", port ));
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    /**
     *
     */
    public void stop(){
        this.stop = true;

    }

    /**
     *
     */
    public void run() {
        while(!stop){
            try {
                selector.select(1000);
                Set<SelectionKey> selectionKeySet = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeySet.iterator();
                SelectionKey key = null;
                while(iterator.hasNext()){
                    key = iterator.next();
                    iterator.remove();
                    try {
                        handleInput(key);
                    }catch (Exception e){
                        if(key != null){
                            key.cancel();
                            if(key.channel()  != null){
                                key.channel().close();
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if(selector != null){
            try {
                selector.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     *
     * @param key
     * @throws IOException
     */
    private void handleInput(SelectionKey key) throws IOException{
        if(key.isValid()){
            // 处理新接入的请求消息
            if(key.isAcceptable()){
                ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
                SocketChannel socketChannel = serverSocketChannel.accept();
                socketChannel.configureBlocking(false);
                socketChannel.register(selector,SelectionKey.OP_READ);
                System.out.println(String.format("A new NIO socket accept: [%s]", socketChannel.toString() ));
            }
            if(key.isReadable()){
                SocketChannel socketChannel = (SocketChannel)key.channel();
                ByteBuffer readBuffer = ByteBuffer.allocate(Constant.BYTE_BUFFER_CAPACITY);
                int readBytes = socketChannel.read(readBuffer);
                if(readBytes > 0){
                    readBuffer.flip();
                    byte[] bytes = new byte[readBuffer.remaining()];
                    readBuffer.get(bytes);
                    String body = new String(bytes,"UTF-8");
                    System.out.println(String.format("Time server receive query [%s]", body));
                    String currenTime = "time".equalsIgnoreCase(body) ? System.currentTimeMillis() + "" : "BAD REQUEST";
                    doWrite(socketChannel,currenTime);
                }else if (readBytes < 0){
                    key.cancel();
                    socketChannel.close();
                }else{

                }
            }
        }
    }

    /**
     *
     * @param socketChannel
     * @param response
     * @throws IOException
     */
    private void doWrite(SocketChannel socketChannel,String response) throws IOException{
        if(response != null
                && !"".equals(response.trim())){
            byte[] bytes = response.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            socketChannel.write(writeBuffer);
        }
    }
}
